/**
 * The Woosta Table Football Engine
 *
 * License... etc.
 **
 * Game objects and events (base classes).
 **
 * Author: Sean Micklethwaite
 **/


#ifndef __gameH__
#define __gameH__

#include "kernel.h"
#include "string.h"
#include "buf.h"
#include "loggable.h"
//#include "serializable.h"
#include <map>

#define OBJECT_EVENT	EVENTCLS_OBJECT


namespace WtfEngine
{
	class IGameObject: public virtual IGcObject {
	public:
		virtual void Destroy() = 0;
		
		/// Recalculate the serialized size of the object.
		/// @note	Classes may cache this size for later use
		///			in Serialize and Deserialize, as this method
		///			is always called beforehand.
		virtual unsigned CalculateSerializedSize() = 0;

		/// Saves the object state into the passed serial buffer
		/// @returns The size of the buffer written (i.e. start offset for subclass)
		virtual unsigned Serialize(void * pBuf) = 0;

		/// Loads the object state from the passed serial buffer
		/// @returns The size of the buffer consumed (i.e. start offset for subclass)
		virtual unsigned Deserialize(void * pBuf) = 0;

		virtual NonCopyBuffer<>::tRef Serialize() = 0;

		virtual bool isOwned() const = 0;
		virtual StringPool::Entry getName() const = 0;

		GC_INTERFACE(IGameObject);
	};

	class GameObject : public virtual IGameObject, public GcObject
	{
		/// True if the current node (this machine) owns the
		/// object.
		bool mIsOwner;
		
		/// The total interest in this game object, used to work
		/// out which objects are in the node's relevant set.
		/// If the interest is zero, the object is not updated.
		/// Objects with a higher interest are generally
		/// allocated a higher ratio of network bandwidth.
		/// @note	Unused when the node is the object's owner.
		tScalar mInterest;
		
		/// The network priority. 
		tScalar mPriority;
		
		StringPool::Entry msName;

	public:
		typedef struct GameObjectCreationArgs {
			StringPool::Entry sName;
			bool 	IsOwner;
			tScalar	Priority;
			
			GameObjectCreationArgs():
				IsOwner(true), Priority(1.0) {
			};
			
		} tCreationOptions;
		
		GameObject(const tCreationOptions& args);
		virtual ~GameObject() {};
		
		virtual void Destroy();
		
		/// Recalculate the serialized size of the object.
		/// @note	Classes may cache this size for later use
		///			in Serialize and Deserialize, as this method
		///			is always called beforehand.
		virtual unsigned CalculateSerializedSize();

		/// Saves the object state into the passed serial buffer
		/// @returns The size of the buffer written (i.e. start offset for subclass)
		virtual unsigned Serialize(void * pBuf);

		/// Loads the object state from the passed serial buffer
		/// @returns The size of the buffer consumed (i.e. start offset for subclass)
		virtual unsigned Deserialize(void * pBuf);

		NonCopyBuffer<>::tRef Serialize();


		bool isOwned() const {
			return mIsOwner;
		};

		StringPool::Entry getName() const {
			return msName;
		};
		virtual String getTypeName() const {
			return "GameObject";
		};

		/// Factory function
		static GameObject * CreateObject(const StringPool::Entry& sName, bool bIsOwner) {
			tCreationOptions opts;
			opts.sName = sName;
			opts.IsOwner = bIsOwner;
			return new GameObject(opts);
		};

		GC_AUTOSIZE(GameObject);
	};
	
	
	/**
	 * An event representing all actions upon a particular
	 * object.
	 **/

	class ObjectEvent : public KeyedEvent<ObjectEvent>
	{
	protected:
		IGameObject::tRef	rObject;
		
	public:
		explicit ObjectEvent(const IGameObject::tRef& pObj): rObject(pObj) {};
		
		IGameObject * getObject() const {
			return &*rObject;
		};
		
		template <class T>
		T * getObject() const{
			return dynamic_cast<T*>(&*rObject);
		};
		
		
		/**
		 * All object events and object interaction events which involve this object
		 * apply here.
		 **/
		EVENT(ObjectEvent, KeyedEvent<ObjectEvent>, ((int)rObject.getRef() - (int)pEv->rObject.getRef()));
	};


	/**
	 * Thrown when an object is destroyed, before it is removed from the game manager.
	 **/
	class ObjectDestroyEvent: public KeyedEvent<ObjectDestroyEvent> {
	protected:
		IGameObject::tRef	rObject;
		
	public:
		explicit ObjectDestroyEvent(const IGameObject::tRef& pObj): rObject(pObj) {};
		
		IGameObject * getObject() const {
			return &*rObject;
		};
		
		EVENT(ObjectDestroyEvent, KeyedEvent<ObjectDestroyEvent>, ((int)rObject.getRef() - (int)pEv->rObject.getRef()));
	};

	EVENT_TYPE_FORK(KeyedObjectEvent, ObjectEvent, const IGameObject::tRef& pObj, pObj);


	/**
	 * An event representing an interaction between two objects, a subset of
	 * ObjectEvents on one object.
	 **/
	class ObjectInteractionEvent : public KeyedObjectEvent<ObjectInteractionEvent>
	{
	protected:
		IGameObject::tRef	rInteractingObject;

	public:
		ObjectInteractionEvent(const IGameObject::tRef& obj, const IGameObject::tRef& intObj)
		: KeyedObjectEvent<ObjectInteractionEvent>(obj), rInteractingObject(intObj) {};
		
		IGameObject * getInteractingObject() const {
			return &*rInteractingObject;
		};
		
		template <class T>
		T * getInteractingObject() const{
			return dynamic_cast<T*>(&*rInteractingObject);
		};
		
		EVENT(ObjectInteractionEvent, KeyedObjectEvent<ObjectInteractionEvent>,
			((int)rInteractingObject.getRef() - (int)pEv->rInteractingObject.getRef()));
	};

	/**
	 * Object lists are held by various tasks to track the objects they are managing.
	 * For example, the renderer holds a list of all objects to be drawn per-frame.
	 **/
	template <class T>
	class ObjectList : public std::list<T>
	{
	public:
		ObjectList() : std::list<T>() {};

		inline void PushObject(const T& r_obj) {
			this->push_back(r_obj);
		};
		
		inline void RemoveObject(const T& r_obj) {
			this->remove(r_obj);
		};
	};


	/**
	 * Thrown when the GameManager produces an update.
	 **/
	class WorkingSetUpdateEvent: public KeyedEvent<WorkingSetUpdateEvent> {
	public:
		typedef std::map<StringPool::Entry, NonCopyBuffer<>::tRef> tObjectUpdateMap;

	private:
		tObjectUpdateMap mObjectUpdates;

	public:
		inline WorkingSetUpdateEvent(): KeyedEvent<WorkingSetUpdateEvent>() {
		};

		/// Returns the entire (immutable) update map.
		inline const tObjectUpdateMap& getUpdates() const {
			return mObjectUpdates;
		};

		/// Update access operator - returns a reference to the given object's update if found,
		/// otherwise a NULL reference.
		inline NonCopyBuffer<>::tRef operator [] (const StringPool::Entry& sName) const {
			tObjectUpdateMap::const_iterator i = mObjectUpdates.find(sName);
			return (i != mObjectUpdates.end() ? i->second : NonCopyBuffer<>::tRef());
		};

		/// Used to insert updates into the map.
		inline NonCopyBuffer<>::tRef& operator [] (const StringPool::Entry& sName) {
			return mObjectUpdates[sName];
		};

		GC_OBJECT(WorkingSetUpdateEvent, GC_ADDEDSIZE(KeyedEvent<WorkingSetUpdateEvent>, sizeof(tObjectUpdateMap::value_type) * mObjectUpdates.size()));
	};


	class GameManager : public Task<GameManager>, public Loggable<GameManager> {
	public:
		typedef enum {
			/// Only update objects in the relevant set. New objects are
			/// added to the relevant set.
			GAME_UPDATE_RELEVANT = 1,
			/// Update all objects, new objects are added to the working set.
			GAME_UPDATE_ALL = 2,
			/// Replaces all game objects, new objects are added to the working set.
			GAME_REPLACE = 3
		} tDeserializeMode;

		typedef FunctorType<GameObject *, const StringPool::Entry&, bool>::tRef tFactoryFunc;

		typedef std::list<GameObject::tRef>                   tObjectList;
		typedef std::map<StringPool::Entry, GameObject::tRef> tObjectMap;
		typedef std::map<StringPool::Entry, tFactoryFunc> tObjectTypeRegistry;

	private:
		tObjectList mWorkingSet;
		tObjectMap mObjectMap;

		tObjectTypeRegistry mTypeRegistry;

	public:
		GameManager();
		
		inline void AddObject(const GameObject::tRef& rObject) {
			if(rObject->isOwned()) {
				mWorkingSet.push_back(rObject);
			};
			mObjectMap[rObject->getName()] = rObject;
		};

		inline void RemoveObject(const GameObject::tRef& rObject) {
			if(rObject->isOwned()) {
				mWorkingSet.remove(rObject);
			};
			mObjectMap.erase(rObject->getName());
		};

		inline GameObject::tRef LookupObject(const StringPool::Entry& sObject) {
			tObjectMap::const_iterator i = mObjectMap.find(sObject);
			return (i == mObjectMap.end() ? GameObject::tRef() : i->second);
		};

		/// Update a specific object in the relevant set.
		void UpdateObject(const StringPool::Entry& sName, const StringPool::Entry& sType, void * pUpdateData);
		
		GameObject::tRef CreateObject(const StringPool::Entry& sName, const StringPool::Entry& sType, bool bOwned);

		NonCopyBuffer<>::tRef SerializeGame();
		void                  DeserializeGame(NonCopyBuffer<>::tRefa rBuf, tDeserializeMode mode);

		void SaveGameToFile(const String& sFile);
		void LoadGameFromFile(const String& sFile);

		void RegisterType(const StringPool::Entry& sName, tFactoryFunc f);

	protected:
		void Run();

		GC_AUTOSIZE(GameManager);
	};
};

#endif
